// MilkingCows
#include <vector>
#include <fstream>
#include <iostream>
#include <algorithm>

using namespace std;

// 1. 如何表达一头牛开始和结束挤奶的时间
// int cow1Begin, cow1End;
// vector<int> milkingInterval;
// milkingInterval[1]
struct Milking {
  int begin;
  int end;
};

// begin 时间小的 Milking 更小
bool compare(const Milking &l, const Milking &r) {
  return l.begin < r.begin;
}

int main(int argc, char const *argv[])
{
  /* code */
  vector<Milking> records;

  /* 1. Input processing */
  ifstream ifs("MilkingCows.in");

  int n = 0;
  ifs >> n;
  for (int i = 0; i < n; ++i) {
    int b = 0, e = 0;
    ifs >> b >> e;
    Milking m{b, e};
    records.push_back(m);
  }

  sort(records.begin(), records.end(), compare);

  for (Milking m : records) {
    cout << m.begin << " " << m.end << endl;
  }

  // 所有奶牛开始挤奶的时间就排序了

  // curr - 最早开始挤奶的牛的时间
  Milking curr = records.front(); // records[0]
  // 最初有牛挤奶的时间
  int maxMilking = curr.end - curr.begin;
  // 最初没有牛挤奶的时间
  int maxNoMilking = 0;

  // 遍历剩余的牛的挤奶时间
  for (int i = 1; i < records.size(); ++i) {
    // records[i] 每一头牛的挤奶时间段

    if (records[i].begin <= curr.end && records[i].end > curr.end) {
      // 右侧交叉的情况
      // 即：curr.begin --- records[i].begin --- curr.end --- records.end[i]
      // 这种情况，不会影响没有牛挤奶的最大时间
      // 因此，我们只要更新 curr.end，让有牛挤奶的时间段向后延伸即可
      curr.end = records[i].end;
      maxMilking = max(maxMilking, curr.end - curr.begin);
    }
    else if (records[i].begin > curr.end) {
      // 右侧超出的情况
      // 即：curr.begin --- curr.end --- records[i].begin --- records.end[i]
      // 这种情况，我们先处理 maxNoMilking
      maxNoMilking = max(maxNoMilking, records[i].begin - curr.end);

      // 看看是这个右侧超出的挤奶时间长，还是历史积累的挤奶时间长
      maxMilking = max(maxMilking, records[i].end - records[i].begin);

      curr = records[i];
    }
  }

  ofstream ofs("MilkingCows.out");
  ofs << maxMilking << " " << maxNoMilking << endl;
  return 0;
}
